/*******************************************************************************
 * Copyright (c) 2006 CEA List.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     CEA List - initial API and implementation
 *******************************************************************************/
package com.cea.papyrus.core.editpart;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;

import org.eclipse.draw2d.AbsoluteBendpoint;
import org.eclipse.draw2d.Connection;
import org.eclipse.draw2d.ConnectionAnchor;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.Notifier;
import org.eclipse.emf.common.util.EList;
import org.eclipse.gef.EditPolicy;
import org.eclipse.gef.editpolicies.ConnectionEndpointEditPolicy;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.ui.views.properties.IPropertySource;
import org.eclipse.uml2.uml.Deployment;
import org.eclipse.uml2.uml.Element;
import org.eclipse.uml2.uml.ElementImport;
import org.eclipse.uml2.uml.Include;
import org.eclipse.uml2.uml.Manifestation;
import org.eclipse.uml2.uml.ProfileApplication;

import com.cea.papyrus.core.PapyrusPlugin;
import com.cea.papyrus.core.editor.ModelManager;
import com.cea.papyrus.core.editpart.connection.AnchorableGraphEdgeEditPart;
import com.cea.papyrus.core.figures.LinkFigure;
import com.cea.papyrus.core.figures.connection.EdgeFigure;
import com.cea.papyrus.core.helper.ModelerHelper;
import com.cea.papyrus.core.listener.GraphElementListener;
import com.cea.papyrus.core.listener.UmlElementDestroyListener;
import com.cea.papyrus.core.listener.UmlElementListener;
import com.cea.papyrus.core.preference.ProfileApplicationPreferenceConstants;
import com.cea.papyrus.core.ui.properties.advanced.EObjectPropertySource;
import com.cea.papyrus.core.utils.Debug;
import com.cea.papyrus.diagraminterchange2.di2.GraphEdge;
import com.cea.papyrus.diagraminterchange2.di2.Property;
import com.cea.papyrus.diagraminterchange2.di2.Uml1SemanticModelBridge;

/**
 * The Class GraphEdgeEditPart.
 * 

 */
public class GraphEdgeEditPart extends AnchorableGraphEdgeEditPart 
implements PropertyChangeListener, IUMLElementEditPart {

	/** The _adapter. */
	protected UmlElementListener _adapter ;

	/** The graph element listener. */
	protected  GraphElementListener graphElementListener;
	
	/** listen destroty evt**/
	protected UmlElementDestroyListener destroyListener;

	/** The channel. */
	protected int channel = 12;

	/** The property source. */
	protected IPropertySource propertySource = null;

	/** The model manager. */
	protected ModelManager modelManager;

	/** The anchor. */
	protected ConnectionAnchor anchor;

	/**
	 * The Constructor.
	 * 
	 * @param graphEdge the graph edge
	 * @param modelManager the model manager
	 */
	public GraphEdgeEditPart(GraphEdge graphEdge, ModelManager modelManager)
	{
		setModel(graphEdge);
		this.modelManager=modelManager;
		_adapter=new UmlElementListener(this);
		this.graphElementListener= new GraphElementListener(this);
		destroyListener=new UmlElementDestroyListener(this);
	}

	/**
	 * Gets the model manager.
	 * 
	 * @return the model manager of the edit part
	 */
	public ModelManager getModelManager(){
		return modelManager;
	}
	/* (non-Javadoc)
	 * @see org.eclipse.gef.editparts.AbstractEditPart#createEditPolicies()
	 */
	/**
	 * Creates the edit policies.
	 */
	@Override
	protected void createEditPolicies() {
		super.createEditPolicies();
//		Selection handle edit policy. 
//		Makes the connection show a feedback, when selected by the user.
		installEditPolicy(EditPolicy.CONNECTION_ENDPOINTS_ROLE,
				new ConnectionEndpointEditPolicy());
		installEditPolicy(
				EditPolicy.CONNECTION_BENDPOINTS_ROLE,
				new BendPointEditPolicy());
//		Allows the removal of the connection model element
		installEditPolicy(EditPolicy.CONNECTION_ROLE, new GraphEdgeEditPolicy());
//		Allow connection on connections
		installEditPolicy(EditPolicy.GRAPHICAL_NODE_ROLE, new EdgeConnectionsEditPolicy());
	}

	/**
	 * Property change.
	 * 
	 * @param event the event
	 */
	public void propertyChange(PropertyChangeEvent event) {
		String property = event.getPropertyName();
//		refreshVisuals();
		refreshBendpoints();
	}
	/* (non-Javadoc)
	 * @see org.eclipse.gef.editparts.AbstractGraphicalEditPart#createFigure()
	 */
	/**
	 * Creates the figure.
	 * 
	 * @return the I figure
	 */
	@Override
	protected IFigure createFigure() {
		return new LinkFigure();
	}

	/**
	 * Return the stereotype presentation (String) or "".
	 * 
	 * @return the presentation
	 */
	protected String getPresentation() {
		String presentation = "";
		String default_presentation = ModelerHelper.TEXT_STEREOTYPE_PRESENTATION; 
		if ((getGraphEdge() == null) || (getGraphEdge().getSemanticModel() == null)) {
			return default_presentation;
		}
		if (getGraphEdge().getSemanticModel().getPresentation() != null) {
			presentation = getGraphEdge().getSemanticModel().getPresentation();
		} else {
			getGraphEdge().getSemanticModel().setPresentation(default_presentation);
			presentation = default_presentation;
		}
		return presentation;
	}

	/**
	 * Gets the graph edge.
	 * 
	 * @return the graph edge
	 */
	public GraphEdge getGraphEdge() {
		return (GraphEdge) getModel();
	}

	/* (non-Javadoc)
	 * @see com.cea.papyrus.core.common.editpart.IUMLElementEditPart#getUmlElement()
	 */
	/**
	 * Gets the uml element.
	 * 
	 * @return the uml element
	 */
	public Element getUmlElement() {
		if (getGraphEdge().getSemanticModel()==null) {
			return null;
		}
		return ((Uml1SemanticModelBridge)getGraphEdge().getSemanticModel()).getElement();
	}
	/* (non-Javadoc)
	 * @see org.eclipse.ui.IActionFilter#testAttribute()
	 */
	/**
	 * Test attribute.
	 * 
	 * @param value the value
	 * @param target the target
	 * @param name the name
	 * 
	 * @return true, if test attribute
	 */
	public boolean testAttribute(Object target, String name, String value) {
		if (target instanceof GraphEdgeEditPart) {
			Element targetUmlElement = ((GraphEdgeEditPart) target).getUmlElement();
			return umlFilter.testAttribute(targetUmlElement, name, value);
		}
		return false;
	}
	/* (non-Javadoc)
	 * @see com.cea.papyrus.core.common.editpart.IUMLElementEditPart#getUmlElement()
	 */
	/**
	 * Checks if is element import.
	 * 
	 * @return true, if is element import
	 */
	public boolean isElementImport() {
		if (getGraphEdge().getSemanticModel()==null) {
			return false;
		}
		return  (((Uml1SemanticModelBridge)getGraphEdge().getSemanticModel()).getElement() instanceof ElementImport);
	}
	/*
	 * (non-Javadoc)
	 * @see com.cea.papyrus.core.common.editpart.IUMLElementEditPart#getElementImport()
	 */
	/**
	 * Gets the element import.
	 * 
	 * @return the element import
	 */
	public ElementImport getElementImport(){
		if (getGraphEdge().getSemanticModel()==null) {
			return null;
		}
		if (((Uml1SemanticModelBridge)getGraphEdge().getSemanticModel()).getElement() instanceof ElementImport){
			return (ElementImport)((Uml1SemanticModelBridge)getGraphEdge().getSemanticModel()).getElement();
		}
		return null;
	}

	/**
	 * Upon activation, attach to the model element as a property change listener.
	 */
	@Override
	public void activate() {
		if (!isActive()) {
			super.activate();
			((Notifier)this.getGraphEdge()).eAdapters().add(graphElementListener);
			if (getUmlElement()!=null){
				((Notifier)getUmlElement()).eAdapters().add(_adapter);
				((Notifier)getUmlElement()).eAdapters().add(destroyListener);}
		}
	}

	/**
	 * Upon deactivation, detach from the model element as a property change listener.
	 */
	@Override
	public void deactivate() {
		if (isActive()) {

			((Notifier)this.getGraphEdge()).eAdapters().remove(graphElementListener);

			if (getUmlElement()!=null) {
				((Notifier)getUmlElement()).eAdapters().remove(_adapter);
				((Notifier)getUmlElement()).eAdapters().remove(destroyListener);
			}
			
			super.deactivate();
		}
	}

	/**
	 * when the mole is changed.
	 * 
	 * @param notification the notification
	 */
	public void notifyChanged(Notification notification) {
		super.notifyChanged(notification);
//		Take care, this one is now called !!!
		refreshVisuals();
	}

	/**
	 * Activate figure.
	 */
	@Override
	public void activateFigure(){
		super.activateFigure();
		/*Once the figure has been added to the ConnectionLayer, start listening for its
		 * router to change.
		 */
		getFigure().addPropertyChangeListener(Connection.PROPERTY_CONNECTION_ROUTER, this);
	}

	/**
	 * Refresh bendpoints.
	 */
	protected void refreshBendpoints() {
		List bendpoints =
			((GraphEdge) getModel()).getWaypoints();
		List constraint = new ArrayList();
		for (int i = 0; i < bendpoints.size(); i++) {
			constraint.add(new AbsoluteBendpoint((Point) bendpoints.get(i)));
		}
		getConnectionFigure().setRoutingConstraint(constraint);
	}

	/**
	 * Refresh visuals.
	 */
	@Override
	protected void refreshVisuals() {
		Debug.debug(this,"refreshVisual", channel);
//		refresh edge figure 
		EdgeFigure fig=(EdgeFigure)getFigure();
		//if(fig != null) {
		if(getGraphEdge() != null) {
			(fig).refreshColors(getGraphEdge());
		}

		if ( getUmlElement()!=null){
			com.cea.papyrus.umlutils.Element currentElement = new com.cea.papyrus.umlutils.Element(getUmlElement());
			(fig).refreshPresentation(getPresentation(), getUmlElement(), getGraphEdge());
			boolean hasIcon  = currentElement.hasIcon();
			boolean hasShape = currentElement.hasShape();
			fig.refreshStereotypes(currentElement.getStereotypes(), getPresentation(), hasIcon, hasShape);
		}
		refreshBendpoints();
		//}
}

	/**
	 * Gets the adapter.
	 * 
	 * @param adapter the adapter
	 * 
	 * @return the adapter
	 */
	@Override
	public Object getAdapter(Class adapter) {
		/* override the default behavior defined in AbstractEditPart
		 * which would expect the model to be a property sourced. 
		 * instead the editpart can provide a property source
		 */
		if (IPropertySource.class == adapter)
		{
			return getPropertySource();
		}
		return super.getAdapter(adapter);
	}
	/* (non-Javadoc)
	 * @see com.ibm.itso.sal330r.gefdemo.edit.NetworkElementEditPart#getPropertySource()
	 */
	/**
	 * Gets the property source.
	 * 
	 * @return the property source
	 */
	protected IPropertySource getPropertySource() {
		if( propertySource == null ) {
			propertySource = new EObjectPropertySource(getUmlElement());
		}
		return propertySource;
	}

	/**
	 * Horizontal stereotypes to display.
	 * 
	 * @return the string
	 */
	public String horizontalStereotypesToDisplay() {
		return stereotypesToDisplay(" ");
	}

	/**
	 * Vertical stereotypes to display.
	 * 
	 * @return the string
	 */
	public String verticalStereotypesToDisplay() {
		return stereotypesToDisplay("\n");
	}

	/**
	 * Stereotypes to display.
	 * 
	 * @param separator the separator
	 * 
	 * @return the string
	 */
	public String stereotypesToDisplay(String separator) {
		EList displayStereotypesList = getGraphEdge().getProperty();
		Iterator<Property> iter = displayStereotypesList.iterator();
		String stereotypesToDisplay="";

		//Get the preference  from PreferenceStore
		IPreferenceStore store = PapyrusPlugin.getDefault().getPreferenceStore();		
		String sNameAppearance = store.getString(ProfileApplicationPreferenceConstants.P_STEREOTYPE_NAME_APPEARANCE);

		if (getUmlElement() instanceof org.eclipse.uml2.uml.Extend) {
			stereotypesToDisplay = "extend,"+separator;
		} else if (getUmlElement() instanceof Include) {
			stereotypesToDisplay = "include,"+separator;
		}else if (getUmlElement() instanceof Deployment) {
			stereotypesToDisplay = "deploy,"+separator;
		} else if (getUmlElement() instanceof Manifestation) {
			stereotypesToDisplay = "manifest,"+separator;
		}else if (getUmlElement() instanceof ProfileApplication) {
			stereotypesToDisplay = "apply,"+separator;
		} 
		while(iter.hasNext()){
			Property prop = iter.next();
			if(prop.getKey().equals(ModelerHelper.PROPERTY_STEREOTYPE_DISPLAY)){
				String qualifiedName = prop.getValue();
//				property value contains qualifiedName ==> extract name from it
				StringTokenizer strToken = new StringTokenizer(qualifiedName, "::");
				String name="";
				while(strToken.hasMoreTokens()) {
					name = strToken.nextToken();
				}
				
				//to show an upper case initial letter for stereotypes
				//as in GraphNodeSemanticEditPart.stereotypesToDisplay()
				if (sNameAppearance.equals(ProfileApplicationPreferenceConstants.P_STEREOTYPE_NAME_DISPLAY_USER_CONTROLLED)) {
					stereotypesToDisplay = stereotypesToDisplay+name+","+separator;	
				} else if ( sNameAppearance.equals(ProfileApplicationPreferenceConstants.P_STEREOTYPE_NAME_DISPLAY_UML_CONFORM)) {
					stereotypesToDisplay = stereotypesToDisplay+name.substring(0, 1).toLowerCase()+name.substring(1, name.length())+","+separator;
				} else { // should not happen since radio button are used to set choice 
					stereotypesToDisplay = stereotypesToDisplay+name.substring(0, 1).toLowerCase()+name.substring(1, name.length())+","+separator;
				}				
			}
		}
		if (!(stereotypesToDisplay.equals(""))) {
			stereotypesToDisplay = stereotypesToDisplay.substring(0, stereotypesToDisplay.length()-2);
		}
		return stereotypesToDisplay;
	}
}
